Overview

Preparing data

Treemap

Both counts have the same genes analysed. These amount to nearly 54,000 genes, which corresponds to the whole genome of mice.

gene_biotypes <- table(gastroc$gene_biotype) %>%
  as.data.frame() %>%
  arrange(desc(Freq))

treemap::treemap(gene_biotypes,
                 index = "Var1",
                 vSize = "Freq",
                 title = "Gene biotypes")

Filtering

Overview

For a broad overview all genes with rowSums() < 10 will be displayed. This is usually similar to omitting genes with zero counts of more than 30%

rs.gastroc <- rowSums(counts.gastroc) %>%
  as.data.frame() %>%
  dplyr::rename("rowSums" = ".")
n.omit.gastroc <- sum(rs.gastroc < 10)
title <- paste0("rowSums low expressed genes, gastroc \n",
                n.omit.gastroc, " genes will be omitted (~",
                floor(n.omit.gastroc / nrow(rs.gastroc) * 100), " %)")

p.gastroc <- filter(rs.gastroc, rowSums < 20) %>%
  ggplot(., aes(x=rowSums))+
  geom_histogram(color="black", fill="white")+
  geom_vline(aes(xintercept=10, color="red"),
             linetype="dashed") +
  ggtitle(title) + 
  theme(legend.position = "none")

# ---
rs.soleus <- rowSums(counts.soleus) %>%
  as.data.frame() %>%
  dplyr::rename("rowSums" = ".")
n.omit.soleus <- sum(rs.soleus < 10)
title <- paste0("rowSums low expressed genes, soleus \n",
                n.omit.soleus, " genes will be omitted (~",
                floor(n.omit.gastroc / nrow(rs.gastroc) * 100), " %)")

p.soleus <- filter(rs.soleus, rowSums < 20) %>%
  ggplot(., aes(x=rowSums))+
  geom_histogram(color="black", fill="white")+
  geom_vline(aes(xintercept=10, color="red"),
             linetype="dashed") +
  ggtitle(title)+ 
  theme(legend.position = "none")

p.gastroc

p.soleus

omitting genes (actual filtering)

omitting genes with zero counts in more than 30% of the samples

Quality assessment

Library sizes

  • too many zero counts
  • different total counts
plot.library_size <- function(counts, title="") {
  cs <- colSums(counts) %>%
    as.data.frame() %>%
    dplyr::rename("colsums" = ".") %>%
    tibble::rownames_to_column(var = "sample")
  
  ggplot(cs, aes(x = sample, y = colsums)) +
    geom_bar(stat = "identity") +
    theme(axis.text.x=element_text(color = "black", angle=45, vjust=.8, hjust=0.8)) + 
    ggtitle(paste0("total counts, ", title))
}


plt.libsize.gastroc <- plot.library_size(counts.gastroc, title = "gastroc") +
  ylim(0, 4e07)
plt.libsize.soleus <- plot.library_size(counts.soleus, title = "soleus") +
  ylim(0, 4e07)

ggpubr::ggarrange(plt.libsize.gastroc, plt.libsize.soleus,
                    ncol = 1, nrow = 2)

not sure what to make of this, but its there.

count distribution boxplots

Preparing the sample by applying log2 and loading metadata files

Resulting Plots:

plotBoxLog <- function(logcounts, metadata) {
  stack(logcounts) %>%
    merge(metadata, by.x = "ind", by.y = "sample_name") %>% 
    ggplot( aes(x=ind, y=values, color=genotype)) +
    geom_boxplot() + 
    ylab("Log2(Counts)") +
    xlab(element_blank()) +
    theme(axis.text.x=element_text(angle=45, vjust=.8, hjust=0.8)) +
    geom_hline(yintercept = median(as.matrix(logcounts.gastroc)), col="blue" )
}

p1 <- plotBoxLog(logcounts.gastroc, metadata.gastroc) + 
  ggtitle("gastroc")
p2 <- plotBoxLog(logcounts.soleus, metadata.soleus) + 
  ggtitle("soleus") +
  theme(axis.ticks.y = element_blank(),
        axis.text.y = element_blank()) +
  rremove("ylab")

using DESeq2::rlog to normalize

Just for exploration purpose, but the resulting data is not used in the following analysis.

p1 <- DESeq2::rlog(as.matrix(counts.gastroc)) %>%
  as.data.frame() %>%
  plotBoxLog(metadata.gastroc) + 
  ggtitle("gastroc") +
  ylab("rlog(counts)")

p2 <- DESeq2::rlog(as.matrix(counts.soleus)) %>%
  as.data.frame() %>%
  plotBoxLog(metadata.soleus) + 
  ggtitle("soleus") +
  theme(axis.ticks.y = element_blank(),
        axis.text.y = element_blank()) +
  rremove("ylab")

most variable genes (top 1000)

To obtain the most variable genes, a z-score will be applied to then take the sd and filter for the top 1000

\(\frac{x - mean(x)}{sd(x)}\)

mostVariableRows <- function(M, ntop=1000) {
  s <- apply( M,1,sd )      # standard deviation
  µ <- apply( M, 1, mean )  # mean
  M.z <- (M - µ) / s         # zscore
  
  # sd ordered descending
  sdev <- apply(counts.gastroc, 1, sd)
  M.top <- M[order(sdev, decreasing = T)[1:ntop] , ]
  return(M.top)
}

counts.gastroc.top <- mostVariableRows(counts.gastroc)
counts.soleus.top <- mostVariableRows(counts.soleus)

visualization with Heatmaps

To visualize the top 1000 most variable genes, I tried to generate some Heatmaps. (This time not using DESeq2::rlog, but just log2(counts). Using the raw counts I could try to just apply log2 to all counts and then create a heatmap.

ComplexHeatmap

The following Heatmaps are done with ComplexHeatmap but are very bad looking.

gastroc

# Get some nicer colours
mypalette <- brewer.pal(11, "RdYlBu")
# http://colorbrewer2.org/#type=sequential&scheme=BuGn&n=3
morecols <- colorRampPalette(mypalette)


chm.gastroc <- Heatmap(
    log2(counts.gastroc.top + 1) %>% as.matrix(),
    row_title = "genes",
    name = "log2",
    show_row_names = FALSE,
    column_title = "samples",
    col = rev(morecols(50)),
    column_title_side = "bottom"
    # row_dend_side = "right",
  )

# saving the CHM as png, to adjust the resolution (I don't know another method)
png(
  "./plots/01_CHM_top_variable_genes.gastroc.png",
  width = 10,
  height = 7.5,
  res = 150,
  units = "in"
)
draw(chm.gastroc)
invisible(dev.off())

soleus

chm.soleus <- Heatmap(
    log2(counts.soleus.top + 1) %>% as.matrix(),
    row_title = "genes",
    name = "log2",
    show_row_names = FALSE,
    column_title = "samples",
    col = rev(morecols(50)),
    column_title_side = "bottom"
    # row_dend_side = "right",
  )

# saving the CHM as png, to adjust the resolution (I don't know another method)
png(
  "./plots/01_CHM_top_variable_genes.soleus.png",
  width = 10,
  height = 7.5,
  res = 150,
  units = "in"
)
draw(chm.soleus)
invisible(dev.off())

gplots::heatmap.2

With this package, the heatmaps are seemingly generated differently, even though I thought both packages use hclust(). Not sure yet what is the difference.

# Set up colour vector for celltype variable
col.cell <- rep(c("#00C3C6"), 12)
col.cell[metadata.gastroc$genotype == "KO"] = "#FF6C67"

heatmap.2(log2(as.matrix(counts.gastroc.top + 1)),
          labRow = FALSE,
          col=rev(morecols(50)),
          ColSideColors=col.cell, scale="row",
          trace="column",
          main="gastroc",
          lmat=rbind(c(5,4), c(3,2), c(0,1)),
          lhei=c(2.2,4,0.2))


heatmap.2(log2(as.matrix(counts.soleus.top + 1)), 
          labRow = FALSE,
          col=rev(morecols(50)),
          ColSideColors=col.cell, scale="row",
          trace="column",
          main="soleus",
          lmat=rbind(c(5,4), c(3,2), c(0,1)),
          lhei=c(2.2,4,0.2))

PCA

gastroc

the pca is performed on the raw data (not z-scored) => Thus using the scale.=T argument?

pca <- prcomp(t(counts.gastroc.top), scale. = T)

pca.data <- data.frame(Sample = rownames(pca$x),
                       X = pca$x[, 1],
                       Y = pca$x[, 2]) %>%
  mutate(type = substr(Sample, 1,2))

plt <-
  autoplot(
    pca,
    data = pca.data,
    colour = 'type',
    label.show.legend = FALSE
  ) +
  ggtitle("PCA gastroc")
# ggsave(filename = "./plots/04_pca_gastroc_filtered.png", plt, dpi=300)
plt

soleus

both tissues

DESeq

preparing DDS objects

createDDSObject <- function(counts, metadata) {
  # select sample columns
  reorder_index <- match(rownames(metadata), colnames(counts))
  counts <- counts[,reorder_index]
  
  # Check metadata consistency
  all(rownames(metadata) %in% colnames(counts)) %>%
    assertthat::assert_that(., msg = "metadata and count table do not match")
  
  ## DESeq2 object
  dds <- DESeqDataSetFromMatrix(countData = counts,
                                colData = metadata,
                                design = ~ genotype)
  return( DESeq(dds) )
}

# creating DESeq Objects
dds.gastroc <- createDDSObject(counts.gastroc, metadata.gastroc)
dds.soleus <- createDDSObject(counts.soleus, metadata.soleus)

MA-Plots

gastroc

plotMA(dds.gastroc)

soleus

plotMA(dds.soleus)

Dispersion Estimates

gastroc

plotDispEsts(dds.gastroc)

soleus

plotDispEsts(dds.soleus)

Results

head(res.gastroc)
log2 fold change (MLE): genotype WT vs KO 
Wald test p-value: genotype WT vs KO 
DataFrame with 6 rows and 6 columns
                    baseMean log2FoldChange     lfcSE      stat      pvalue        padj
                   <numeric>      <numeric> <numeric> <numeric>   <numeric>   <numeric>
ENSMUSG00000057003   1263134       1.528571  0.188761   8.09790 5.59144e-16 1.11629e-14
ENSMUSG00000064351   1072094       0.464631  0.154018   3.01674 2.55513e-03 7.20561e-03
ENSMUSG00000031972    901274       0.915253  0.102929   8.89212 5.99562e-19 1.64640e-17
ENSMUSG00000061723    723692       1.407953  0.123315  11.41751 3.41876e-30 2.64747e-28
ENSMUSG00000032366    645951       1.635209  0.155699  10.50238 8.42310e-26 4.55336e-24
ENSMUSG00000030695    606244       0.605202  0.114231   5.29807 1.17031e-07 7.66958e-07
head(res.soleus)
log2 fold change (MLE): genotype WT vs KO 
Wald test p-value: genotype WT vs KO 
DataFrame with 6 rows and 6 columns
                     baseMean log2FoldChange     lfcSE      stat    pvalue      padj
                    <numeric>      <numeric> <numeric> <numeric> <numeric> <numeric>
ENSMUSG00000051951   0.877455      0.4953332  1.516260  0.326681 0.7439092        NA
ENSMUSG00000025900 801.345381      0.3712948  0.160227  2.317311 0.0204868  0.124199
ENSMUSG00000025902 875.547200     -0.0583178  0.198690 -0.293512 0.7691312  0.903197
ENSMUSG00000104238   1.556245      0.7166751  1.103738  0.649316 0.5161341        NA
ENSMUSG00000102269  11.634924      0.6967718  0.450394  1.547027 0.1218568  0.361899
ENSMUSG00000096126   1.623686     -1.0224528  0.965597 -1.058881 0.2896538        NA

top significant diff. genes

topGenes.gastroc <- as.data.frame(res.gastroc) %>%
  tibble::rownames_to_column("GeneID") %>%
  top_n(100, wt = -padj) %>%
  arrange(padj)

topGenes.soleus <- as.data.frame(res.soleus) %>%
  tibble::rownames_to_column("GeneID") %>%
  top_n(100, wt = -padj) %>%
  arrange(padj)

knitr::kable(head(topGenes.gastroc), caption = "gastroc")
gastroc
GeneID baseMean log2FoldChange lfcSE stat pvalue padj
ENSMUSG00000050335 4934.2949 -6.267396 0.1664065 -37.66316 0 0
ENSMUSG00000081249 1378.5460 -4.208222 0.1115955 -37.70959 0 0
ENSMUSG00000010751 1864.4937 -5.610357 0.2014267 -27.85309 0 0
ENSMUSG00000032712 5136.5059 -4.686418 0.1683204 -27.84225 0 0
ENSMUSG00000034855 554.3475 -7.276611 0.2735037 -26.60517 0 0
ENSMUSG00000037613 960.7558 -3.183338 0.1266510 -25.13472 0 0
knitr::kable(head(topGenes.soleus), caption = "soleus")
soleus
GeneID baseMean log2FoldChange lfcSE stat pvalue padj
ENSMUSG00000038615 32047.185 2.5454285 0.0636829 39.97038 0 0
ENSMUSG00000081249 1651.592 -4.5759032 0.1292779 -35.39587 0 0
ENSMUSG00000039067 4355.557 1.1049116 0.0419786 26.32084 0 0
ENSMUSG00000028932 3190.657 1.1102258 0.0558536 19.87741 0 0
ENSMUSG00000006998 7402.617 0.9196943 0.0468631 19.62511 0 0
ENSMUSG00000102869 6985.622 0.9181929 0.0479924 19.13203 0 0

p-values

hist(res.gastroc$pvalue, main = "gastroc")

hist(res.soleus$pvalue, main = "soleus")

shrinking

Is shrinking necessary?

[] watch the course video

Volcano Plot

resTab.gastroc <- as.data.frame(res.gastroc) %>%
  tibble::rownames_to_column("GeneID") %>% 
  # left_join(ensemblAnnot, by = "GeneID") %>% 
  dplyr::rename(logFC = log2FoldChange, FDR = padj) %>%
  mutate(`-log10(pvalue)` = -log10(pvalue))

resTab.soleus <- as.data.frame(res.soleus) %>%
  tibble::rownames_to_column("GeneID") %>% 
  # left_join(ensemblAnnot, by = "GeneID") %>% 
  dplyr::rename(logFC = log2FoldChange, FDR = padj) %>% 
  mutate(`-log10(pvalue)` = -log10(pvalue))
ggplot(resTab.gastroc, aes(x = logFC, y = `-log10(pvalue)`)) +
  geom_point(aes(colour = FDR < 0.05), size = 1) +
  geom_text(data = ~top_n(.x, 1, wt=-FDR), aes(label = GeneID))


ggplot(resTab.soleus, aes(x = logFC, y = `-log10(pvalue)`)) +
  geom_point(aes(colour = FDR < 0.05), size = 1) +
  geom_text(data = ~top_n(.x, 1, wt=-FDR), aes(label = GeneID))

save R ojects

saveRDS(counts.gastroc, file = "./data/Robjects/counts.gastroc.rds")
saveRDS(counts.soleus, file = "./data/Robjects/counts.soleus.rds")
save(dds.gastroc, dds.soleus, file = "./data/Robjects/DDS.RData")

Final Questions

Most variable genes:

PCA:

DESeq:

LS0tCnRpdGxlOiAiRmlyc3RBbmFseXNpcyIKYXV0aG9yOiAiTmljayBEaWVyY2tzZW4iCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICB0b2M6IHllcwogICAgZGZfcHJpbnQ6IHBhZ2VkCmVkaXRvcl9vcHRpb25zOiAKICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKbGlicmFyeShkcGx5cikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHRyZWVtYXApCmxpYnJhcnkoREVTZXEyKQpsaWJyYXJ5KGdnZm9ydGlmeSkgIyBhdXRvcGxvdCAoUENBKQojIGxpYnJhcnkoZ3JpZEV4dHJhKQpsaWJyYXJ5KGdncHVicikKCiMgSGVhdG1hcHMKIyAjIyBHaXRodWIgJiBDb21wbGV4SGVhdG1hcCAtLS0tCmlmICghICJDb21wbGV4SGVhdG1hcCIgJWluJSByb3cubmFtZXMoaW5zdGFsbGVkLnBhY2thZ2VzKCkpKSB7CiAgZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJqb2tlcmdvby9Db21wbGV4SGVhdG1hcCIsIGZvcmNlID0gVFJVRSkKICAjIEJpb2NNYW5hZ2VyOjppbnN0YWxsKCJDb21wbGV4SGVhdG1hcCIpCn0KbGlicmFyeShDb21wbGV4SGVhdG1hcCkKbGlicmFyeShjaXJjbGl6ZSkKbGlicmFyeShSQ29sb3JCcmV3ZXIpCmxpYnJhcnkoZ3Bsb3RzKSAjIChoZWF0bWFwLjIpCmBgYAoKIyBPdmVydmlldwoKIyMgUHJlcGFyaW5nIGRhdGEKCmBgYHtyIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9Cmdhc3Ryb2MgPC0gcmVhZHI6OnJlYWRfdHN2KCIuL2RhdGEvcmVhZGNvdW50X2dlbmVuYW1lX2dhc3Ryb2MueGxzIikgJT4lCiAgdGliYmxlOjpjb2x1bW5fdG9fcm93bmFtZXModmFyID0gImdlbmVfaWQiKQpjb3VudHMuZ2FzdHJvYyA8LSBnYXN0cm9jWywgMToxMl0KCnNvbGV1cyA8LSByZWFkcjo6cmVhZF90c3YoIi4vZGF0YS9yZWFkY291bnRfZ2VuZW5hbWVfc29sZXVzLnhscyIpICU+JQogIHRpYmJsZTo6Y29sdW1uX3RvX3Jvd25hbWVzKHZhciA9ICJnZW5lX2lkIikKY291bnRzLnNvbGV1cyA8LSBzb2xldXNbLCAxOjEyXQoKYGBgCgojIyBUcmVlbWFwCgpCb3RoIGNvdW50cyBoYXZlIHRoZSBzYW1lIGdlbmVzIGFuYWx5c2VkLgpUaGVzZSBhbW91bnQgdG8gbmVhcmx5IDU0LDAwMCBnZW5lcywgd2hpY2ggY29ycmVzcG9uZHMgdG8gdGhlIHdob2xlIGdlbm9tZSBvZiBtaWNlLiAKCmBgYHtyfQpnZW5lX2Jpb3R5cGVzIDwtIHRhYmxlKGdhc3Ryb2MkZ2VuZV9iaW90eXBlKSAlPiUKICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgYXJyYW5nZShkZXNjKEZyZXEpKQoKdHJlZW1hcDo6dHJlZW1hcChnZW5lX2Jpb3R5cGVzLAogICAgICAgICAgICAgICAgIGluZGV4ID0gIlZhcjEiLAogICAgICAgICAgICAgICAgIHZTaXplID0gIkZyZXEiLAogICAgICAgICAgICAgICAgIHRpdGxlID0gIkdlbmUgYmlvdHlwZXMiKQpgYGAKCiMgRmlsdGVyaW5nCgojIyBPdmVydmlldwoKRm9yIGEgYnJvYWQgb3ZlcnZpZXcgYWxsIGdlbmVzIHdpdGggYHJvd1N1bXMoKSA8IDEwYCB3aWxsIGJlIGRpc3BsYXllZC4gVGhpcyBpcyB1c3VhbGx5IHNpbWlsYXIgdG8gb21pdHRpbmcgZ2VuZXMgd2l0aCB6ZXJvIGNvdW50cyBvZiBtb3JlIHRoYW4gMzAlCgpgYGB7cn0KcnMuZ2FzdHJvYyA8LSByb3dTdW1zKGNvdW50cy5nYXN0cm9jKSAlPiUKICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgZHBseXI6OnJlbmFtZSgicm93U3VtcyIgPSAiLiIpCm4ub21pdC5nYXN0cm9jIDwtIHN1bShycy5nYXN0cm9jIDwgMTApCnRpdGxlIDwtIHBhc3RlMCgicm93U3VtcyBsb3cgZXhwcmVzc2VkIGdlbmVzLCBnYXN0cm9jIFxuIiwKICAgICAgICAgICAgICAgIG4ub21pdC5nYXN0cm9jLCAiIGdlbmVzIHdpbGwgYmUgb21pdHRlZCAofiIsCiAgICAgICAgICAgICAgICBmbG9vcihuLm9taXQuZ2FzdHJvYyAvIG5yb3cocnMuZ2FzdHJvYykgKiAxMDApLCAiICUpIikKCnAuZ2FzdHJvYyA8LSBmaWx0ZXIocnMuZ2FzdHJvYywgcm93U3VtcyA8IDIwKSAlPiUKICBnZ3Bsb3QoLiwgYWVzKHg9cm93U3VtcykpKwogIGdlb21faGlzdG9ncmFtKGNvbG9yPSJibGFjayIsIGZpbGw9IndoaXRlIikrCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdD0xMCwgY29sb3I9InJlZCIpLAogICAgICAgICAgICAgbGluZXR5cGU9ImRhc2hlZCIpICsKICBnZ3RpdGxlKHRpdGxlKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCiMgLS0tCnJzLnNvbGV1cyA8LSByb3dTdW1zKGNvdW50cy5zb2xldXMpICU+JQogIGFzLmRhdGEuZnJhbWUoKSAlPiUKICBkcGx5cjo6cmVuYW1lKCJyb3dTdW1zIiA9ICIuIikKbi5vbWl0LnNvbGV1cyA8LSBzdW0ocnMuc29sZXVzIDwgMTApCnRpdGxlIDwtIHBhc3RlMCgicm93U3VtcyBsb3cgZXhwcmVzc2VkIGdlbmVzLCBzb2xldXMgXG4iLAogICAgICAgICAgICAgICAgbi5vbWl0LnNvbGV1cywgIiBnZW5lcyB3aWxsIGJlIG9taXR0ZWQgKH4iLAogICAgICAgICAgICAgICAgZmxvb3Iobi5vbWl0Lmdhc3Ryb2MgLyBucm93KHJzLmdhc3Ryb2MpICogMTAwKSwgIiAlKSIpCgpwLnNvbGV1cyA8LSBmaWx0ZXIocnMuc29sZXVzLCByb3dTdW1zIDwgMjApICU+JQogIGdncGxvdCguLCBhZXMoeD1yb3dTdW1zKSkrCiAgZ2VvbV9oaXN0b2dyYW0oY29sb3I9ImJsYWNrIiwgZmlsbD0id2hpdGUiKSsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PTEwLCBjb2xvcj0icmVkIiksCiAgICAgICAgICAgICBsaW5ldHlwZT0iZGFzaGVkIikgKwogIGdndGl0bGUodGl0bGUpKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgpwLmdhc3Ryb2MKcC5zb2xldXMKCmBgYAoKIyMgb21pdHRpbmcgZ2VuZXMgKGFjdHVhbCBmaWx0ZXJpbmcpCgpvbWl0dGluZyBnZW5lcyB3aXRoIHplcm8gY291bnRzIGluIG1vcmUgdGhhbiAzMCUgb2YgdGhlIHNhbXBsZXMKCmBgYHtyIGVjaG89RkFMU0V9Cm9taXRaZXJvQ291bnRzIDwtIGZ1bmN0aW9uKGNvdW50cy5yYXcsIHNoYXJlPTAuMykgewogIGtlZXAgPC0gKGNvdW50cy5yYXcgIT0gMCkgJT4lCiAgICByb3dTdW1zKCkgPiByb3VuZChzaGFyZSAqIG5jb2woY291bnRzLnJhdykpCiAgcmV0dXJuKGNvdW50cy5yYXdba2VlcCxdKQp9Cgpjb3VudHMuZ2FzdHJvYyA8LSBvbWl0WmVyb0NvdW50cyhjb3VudHMuZ2FzdHJvYykKY291bnRzLnNvbGV1cyA8LSBvbWl0WmVyb0NvdW50cyhjb3VudHMuc29sZXVzKQpgYGAKCgoKIyBRdWFsaXR5IGFzc2Vzc21lbnQKCiMjIExpYnJhcnkgc2l6ZXMKCi0gICB0b28gbWFueSB6ZXJvIGNvdW50cwotICAgZGlmZmVyZW50IHRvdGFsIGNvdW50cwoKYGBge3J9CnBsb3QubGlicmFyeV9zaXplIDwtIGZ1bmN0aW9uKGNvdW50cywgdGl0bGU9IiIpIHsKICBjcyA8LSBjb2xTdW1zKGNvdW50cykgJT4lCiAgICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgICBkcGx5cjo6cmVuYW1lKCJjb2xzdW1zIiA9ICIuIikgJT4lCiAgICB0aWJibGU6OnJvd25hbWVzX3RvX2NvbHVtbih2YXIgPSAic2FtcGxlIikKICAKICBnZ3Bsb3QoY3MsIGFlcyh4ID0gc2FtcGxlLCB5ID0gY29sc3VtcykpICsKICAgIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArCiAgICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoY29sb3IgPSAiYmxhY2siLCBhbmdsZT00NSwgdmp1c3Q9LjgsIGhqdXN0PTAuOCkpICsgCiAgICBnZ3RpdGxlKHBhc3RlMCgidG90YWwgY291bnRzLCAiLCB0aXRsZSkpCn0KCgpwbHQubGlic2l6ZS5nYXN0cm9jIDwtIHBsb3QubGlicmFyeV9zaXplKGNvdW50cy5nYXN0cm9jLCB0aXRsZSA9ICJnYXN0cm9jIikgKwogIHlsaW0oMCwgNGUwNykKcGx0LmxpYnNpemUuc29sZXVzIDwtIHBsb3QubGlicmFyeV9zaXplKGNvdW50cy5zb2xldXMsIHRpdGxlID0gInNvbGV1cyIpICsKICB5bGltKDAsIDRlMDcpCgpnZ3B1YnI6OmdnYXJyYW5nZShwbHQubGlic2l6ZS5nYXN0cm9jLCBwbHQubGlic2l6ZS5zb2xldXMsCiAgICAgICAgICAgICAgICAgICAgbmNvbCA9IDEsIG5yb3cgPSAyKQpgYGAKCm5vdCBzdXJlIHdoYXQgdG8gbWFrZSBvZiB0aGlzLCBidXQgaXRzIHRoZXJlLgoKCiMjIyBjb3VudCBkaXN0cmlidXRpb24gYm94cGxvdHMKClByZXBhcmluZyB0aGUgc2FtcGxlIGJ5IGFwcGx5aW5nIGxvZzIgYW5kIGxvYWRpbmcgbWV0YWRhdGEgZmlsZXMKCmBgYHtyIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CmxvZ2NvdW50cy5nYXN0cm9jIDwtIGxvZzIoY291bnRzLmdhc3Ryb2MgKyAxKSAjIGF2b2lkaW5nIC1JbmYgd2l0aCArKwpsb2djb3VudHMuc29sZXVzIDwtIGxvZzIoY291bnRzLnNvbGV1cyArIDEpICMgYXZvaWRpbmcgLUluZiB3aXRoICsrCgptZXRhZGF0YS5nYXN0cm9jIDwtIHJlYWRyOjpyZWFkX2NzdjIoIi4vZGF0YS9zYW1wbGVfdGFibGVfZ2FzdHJvYy5jc3YiKSAlPiUKICB0aWJibGU6OmNvbHVtbl90b19yb3duYW1lcygicnVuIikKbWV0YWRhdGEuc29sZXVzIDwtIHJlYWRyOjpyZWFkX2NzdjIoIi4vZGF0YS9zYW1wbGVfdGFibGVfc29sZXVzLmNzdiIpICU+JQogIHRpYmJsZTo6Y29sdW1uX3RvX3Jvd25hbWVzKCJydW4iKQpgYGAKCgpSZXN1bHRpbmcgUGxvdHM6CgpgYGB7ciBtZXNzYWdlPUZBTFNFfQpwbG90Qm94TG9nIDwtIGZ1bmN0aW9uKGxvZ2NvdW50cywgbWV0YWRhdGEpIHsKICBzdGFjayhsb2djb3VudHMpICU+JQogICAgbWVyZ2UobWV0YWRhdGEsIGJ5LnggPSAiaW5kIiwgYnkueSA9ICJzYW1wbGVfbmFtZSIpICU+JSAKICAgIGdncGxvdCggYWVzKHg9aW5kLCB5PXZhbHVlcywgY29sb3I9Z2Vub3R5cGUpKSArCiAgICBnZW9tX2JveHBsb3QoKSArIAogICAgeWxhYigiTG9nMihDb3VudHMpIikgKwogICAgeGxhYihlbGVtZW50X2JsYW5rKCkpICsKICAgIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSwgdmp1c3Q9LjgsIGhqdXN0PTAuOCkpICsKICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IG1lZGlhbihhcy5tYXRyaXgobG9nY291bnRzLmdhc3Ryb2MpKSwgY29sPSJibHVlIiApCn0KCnAxIDwtIHBsb3RCb3hMb2cobG9nY291bnRzLmdhc3Ryb2MsIG1ldGFkYXRhLmdhc3Ryb2MpICsgCiAgZ2d0aXRsZSgiZ2FzdHJvYyIpCnAyIDwtIHBsb3RCb3hMb2cobG9nY291bnRzLnNvbGV1cywgbWV0YWRhdGEuc29sZXVzKSArIAogIGdndGl0bGUoInNvbGV1cyIpICsKICB0aGVtZShheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCkpICsKICBycmVtb3ZlKCJ5bGFiIikKYGBgCgoKYGBge3IgZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KcGRmKE5VTEwpICMgYXZvaWRpbmcgYmxhbmsgcGxvdCBjcmVhdGVkIGJ5IGdnYXJyYW5nZQpwIDwtIGdncHVicjo6Z2dhcnJhbmdlKHAxLCBwMiwgbmNvbD0yLCBjb21tb24ubGVnZW5kID0gVFJVRSwgbGVnZW5kID0gInJpZ2h0IikKeCA9IGRldi5vZmYoKSAgIyBzZWUgYWJvdmUKcApgYGAKCgoKIyMjIHVzaW5nIERFU2VxMjo6cmxvZyB0byBub3JtYWxpemUKCkp1c3QgZm9yIGV4cGxvcmF0aW9uIHB1cnBvc2UsIGJ1dCB0aGUgcmVzdWx0aW5nIGRhdGEgaXMgbm90IHVzZWQgaW4gdGhlCmZvbGxvd2luZyBhbmFseXNpcy4KCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CnAxIDwtIERFU2VxMjo6cmxvZyhhcy5tYXRyaXgoY291bnRzLmdhc3Ryb2MpKSAlPiUKICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgcGxvdEJveExvZyhtZXRhZGF0YS5nYXN0cm9jKSArIAogIGdndGl0bGUoImdhc3Ryb2MiKSArCiAgeWxhYigicmxvZyhjb3VudHMpIikKCnAyIDwtIERFU2VxMjo6cmxvZyhhcy5tYXRyaXgoY291bnRzLnNvbGV1cykpICU+JQogIGFzLmRhdGEuZnJhbWUoKSAlPiUKICBwbG90Qm94TG9nKG1ldGFkYXRhLnNvbGV1cykgKyAKICBnZ3RpdGxlKCJzb2xldXMiKSArCiAgdGhlbWUoYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpKSArCiAgcnJlbW92ZSgieWxhYiIpCmBgYAoKCmBgYHtyIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CnBkZihOVUxMKSAjIGF2b2lkaW5nIGJsYW5rIHBsb3QgY3JlYXRlZCBieSBnZ2FycmFuZ2UKcCA8LSBnZ3B1YnI6OmdnYXJyYW5nZShwMSwgcDIsIG5jb2w9MiwgY29tbW9uLmxlZ2VuZCA9IFRSVUUsIGxlZ2VuZCA9ICJyaWdodCIpCnggPSBkZXYub2ZmKCkgICMgc2VlIGFib3ZlCnAKYGBgCgojIG1vc3QgdmFyaWFibGUgZ2VuZXMgKHRvcCAxMDAwKQpUbyBvYnRhaW4gdGhlIG1vc3QgdmFyaWFibGUgZ2VuZXMsIGEgei1zY29yZSB3aWxsIGJlIGFwcGxpZWQgdG8gdGhlbiB0YWtlIHRoZQpzZCBhbmQgZmlsdGVyIGZvciB0aGUgdG9wIDEwMDAKCiRcZnJhY3t4IC0gbWVhbih4KX17c2QoeCl9JAoKYGBge3J9Cm1vc3RWYXJpYWJsZVJvd3MgPC0gZnVuY3Rpb24oTSwgbnRvcD0xMDAwKSB7CiAgcyA8LSBhcHBseSggTSwxLHNkICkgICAgICAjIHN0YW5kYXJkIGRldmlhdGlvbgogIMK1IDwtIGFwcGx5KCBNLCAxLCBtZWFuICkgICMgbWVhbgogIE0ueiA8LSAoTSAtIMK1KSAvIHMgICAgICAgICAjIHpzY29yZQogIAogICMgc2Qgb3JkZXJlZCBkZXNjZW5kaW5nCiAgc2RldiA8LSBhcHBseShjb3VudHMuZ2FzdHJvYywgMSwgc2QpCiAgTS50b3AgPC0gTVtvcmRlcihzZGV2LCBkZWNyZWFzaW5nID0gVClbMTpudG9wXSAsIF0KICByZXR1cm4oTS50b3ApCn0KCmNvdW50cy5nYXN0cm9jLnRvcCA8LSBtb3N0VmFyaWFibGVSb3dzKGNvdW50cy5nYXN0cm9jKQpjb3VudHMuc29sZXVzLnRvcCA8LSBtb3N0VmFyaWFibGVSb3dzKGNvdW50cy5zb2xldXMpCmBgYAoKCiMjIHZpc3VhbGl6YXRpb24gd2l0aCBIZWF0bWFwcwpUbyB2aXN1YWxpemUgdGhlIHRvcCAxMDAwIG1vc3QgdmFyaWFibGUgZ2VuZXMsIEkgdHJpZWQgdG8gZ2VuZXJhdGUgc29tZSBIZWF0bWFwcy4gKFRoaXMgdGltZSBub3QgdXNpbmcgREVTZXEyOjpybG9nLCBidXQganVzdCBgbG9nMihjb3VudHMpYC4gVXNpbmcgdGhlIHJhdyBjb3VudHMKSSBjb3VsZCB0cnkgdG8ganVzdCBhcHBseSBgbG9nMmAgdG8gYWxsIGNvdW50cyBhbmQgdGhlbiBjcmVhdGUgYSBoZWF0bWFwLgoKIyMjIGBDb21wbGV4SGVhdG1hcGAKVGhlIGZvbGxvd2luZyBIZWF0bWFwcyBhcmUgZG9uZSB3aXRoIGBDb21wbGV4SGVhdG1hcGAgYnV0IGFyZSB2ZXJ5IGJhZCBsb29raW5nLgoKIyMjIyBnYXN0cm9jCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQojIEdldCBzb21lIG5pY2VyIGNvbG91cnMKbXlwYWxldHRlIDwtIGJyZXdlci5wYWwoMTEsICJSZFlsQnUiKQojIGh0dHA6Ly9jb2xvcmJyZXdlcjIub3JnLyN0eXBlPXNlcXVlbnRpYWwmc2NoZW1lPUJ1R24mbj0zCm1vcmVjb2xzIDwtIGNvbG9yUmFtcFBhbGV0dGUobXlwYWxldHRlKQoKCmNobS5nYXN0cm9jIDwtIEhlYXRtYXAoCiAgICBsb2cyKGNvdW50cy5nYXN0cm9jLnRvcCArIDEpICU+JSBhcy5tYXRyaXgoKSwKICAgIHJvd190aXRsZSA9ICJnZW5lcyIsCiAgICBuYW1lID0gImxvZzIiLAogICAgc2hvd19yb3dfbmFtZXMgPSBGQUxTRSwKICAgIGNvbHVtbl90aXRsZSA9ICJzYW1wbGVzIiwKICAgIGNvbCA9IHJldihtb3JlY29scyg1MCkpLAogICAgY29sdW1uX3RpdGxlX3NpZGUgPSAiYm90dG9tIgogICAgIyByb3dfZGVuZF9zaWRlID0gInJpZ2h0IiwKICApCgojIHNhdmluZyB0aGUgQ0hNIGFzIHBuZywgdG8gYWRqdXN0IHRoZSByZXNvbHV0aW9uIChJIGRvbid0IGtub3cgYW5vdGhlciBtZXRob2QpCnBuZygKICAiLi9wbG90cy8wMV9DSE1fdG9wX3ZhcmlhYmxlX2dlbmVzLmdhc3Ryb2MucG5nIiwKICB3aWR0aCA9IDEwLAogIGhlaWdodCA9IDcuNSwKICByZXMgPSAxNTAsCiAgdW5pdHMgPSAiaW4iCikKZHJhdyhjaG0uZ2FzdHJvYykKaW52aXNpYmxlKGRldi5vZmYoKSkKYGBgCgohW10oLi9wbG90cy8wMV9DSE1fdG9wX3ZhcmlhYmxlX2dlbmVzLmdhc3Ryb2MucG5nKQoKIyMjIyBzb2xldXMKCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CmNobS5zb2xldXMgPC0gSGVhdG1hcCgKICAgIGxvZzIoY291bnRzLnNvbGV1cy50b3AgKyAxKSAlPiUgYXMubWF0cml4KCksCiAgICByb3dfdGl0bGUgPSAiZ2VuZXMiLAogICAgbmFtZSA9ICJsb2cyIiwKICAgIHNob3dfcm93X25hbWVzID0gRkFMU0UsCiAgICBjb2x1bW5fdGl0bGUgPSAic2FtcGxlcyIsCiAgICBjb2wgPSByZXYobW9yZWNvbHMoNTApKSwKICAgIGNvbHVtbl90aXRsZV9zaWRlID0gImJvdHRvbSIKICAgICMgcm93X2RlbmRfc2lkZSA9ICJyaWdodCIsCiAgKQoKIyBzYXZpbmcgdGhlIENITSBhcyBwbmcsIHRvIGFkanVzdCB0aGUgcmVzb2x1dGlvbiAoSSBkb24ndCBrbm93IGFub3RoZXIgbWV0aG9kKQpwbmcoCiAgIi4vcGxvdHMvMDFfQ0hNX3RvcF92YXJpYWJsZV9nZW5lcy5zb2xldXMucG5nIiwKICB3aWR0aCA9IDEwLAogIGhlaWdodCA9IDcuNSwKICByZXMgPSAxNTAsCiAgdW5pdHMgPSAiaW4iCikKZHJhdyhjaG0uc29sZXVzKQppbnZpc2libGUoZGV2Lm9mZigpKQpgYGAKCiFbXSguL3Bsb3RzLzAxX0NITV90b3BfdmFyaWFibGVfZ2VuZXMuc29sZXVzLnBuZykKCgoKIyMjIGBncGxvdHM6OmhlYXRtYXAuMmAKCldpdGggdGhpcyBwYWNrYWdlLCB0aGUgaGVhdG1hcHMgYXJlIHNlZW1pbmdseSBnZW5lcmF0ZWQgZGlmZmVyZW50bHksIGV2ZW4gdGhvdWdoCkkgdGhvdWdodCBib3RoIHBhY2thZ2VzIHVzZSBgaGNsdXN0KClgLiBOb3Qgc3VyZSB5ZXQgd2hhdCBpcyB0aGUgZGlmZmVyZW5jZS4KCmBgYHtyfQojIFNldCB1cCBjb2xvdXIgdmVjdG9yIGZvciBjZWxsdHlwZSB2YXJpYWJsZQpjb2wuY2VsbCA8LSByZXAoYygiIzAwQzNDNiIpLCAxMikKY29sLmNlbGxbbWV0YWRhdGEuZ2FzdHJvYyRnZW5vdHlwZSA9PSAiS08iXSA9ICIjRkY2QzY3IgoKaGVhdG1hcC4yKGxvZzIoYXMubWF0cml4KGNvdW50cy5nYXN0cm9jLnRvcCArIDEpKSwKICAgICAgICAgIGxhYlJvdyA9IEZBTFNFLAogICAgICAgICAgY29sPXJldihtb3JlY29scyg1MCkpLAogICAgICAgICAgQ29sU2lkZUNvbG9ycz1jb2wuY2VsbCwgc2NhbGU9InJvdyIsCiAgICAgICAgICB0cmFjZT0iY29sdW1uIiwKICAgICAgICAgIG1haW49Imdhc3Ryb2MiLAogICAgICAgICAgbG1hdD1yYmluZChjKDUsNCksIGMoMywyKSwgYygwLDEpKSwKICAgICAgICAgIGxoZWk9YygyLjIsNCwwLjIpKQoKaGVhdG1hcC4yKGxvZzIoYXMubWF0cml4KGNvdW50cy5zb2xldXMudG9wICsgMSkpLCAKICAgICAgICAgIGxhYlJvdyA9IEZBTFNFLAogICAgICAgICAgY29sPXJldihtb3JlY29scyg1MCkpLAogICAgICAgICAgQ29sU2lkZUNvbG9ycz1jb2wuY2VsbCwgc2NhbGU9InJvdyIsCiAgICAgICAgICB0cmFjZT0iY29sdW1uIiwKICAgICAgICAgIG1haW49InNvbGV1cyIsCiAgICAgICAgICBsbWF0PXJiaW5kKGMoNSw0KSwgYygzLDIpLCBjKDAsMSkpLAogICAgICAgICAgbGhlaT1jKDIuMiw0LDAuMikpCmBgYAoKCgoKIyBQQ0EKCiMjIGdhc3Ryb2MKCnRoZSBwY2EgaXMgcGVyZm9ybWVkIG9uIHRoZSByYXcgZGF0YSAobm90IHotc2NvcmVkKQo9PiBUaHVzIHVzaW5nIHRoZSBgc2NhbGUuPVRgIGFyZ3VtZW50PwoKYGBge3IgbWVzc2FnZT1GQUxTRX0KcGNhIDwtIHByY29tcCh0KGNvdW50cy5nYXN0cm9jLnRvcCksIHNjYWxlLiA9IFQpCgpwY2EuZGF0YSA8LSBkYXRhLmZyYW1lKFNhbXBsZSA9IHJvd25hbWVzKHBjYSR4KSwKICAgICAgICAgICAgICAgICAgICAgICBYID0gcGNhJHhbLCAxXSwKICAgICAgICAgICAgICAgICAgICAgICBZID0gcGNhJHhbLCAyXSkgJT4lCiAgbXV0YXRlKHR5cGUgPSBzdWJzdHIoU2FtcGxlLCAxLDIpKQoKcGx0IDwtCiAgYXV0b3Bsb3QoCiAgICBwY2EsCiAgICBkYXRhID0gcGNhLmRhdGEsCiAgICBjb2xvdXIgPSAndHlwZScsCiAgICBsYWJlbC5zaG93LmxlZ2VuZCA9IEZBTFNFCiAgKSArCiAgZ2d0aXRsZSgiUENBIGdhc3Ryb2MiKQojIGdnc2F2ZShmaWxlbmFtZSA9ICIuL3Bsb3RzLzA0X3BjYV9nYXN0cm9jX2ZpbHRlcmVkLnBuZyIsIHBsdCwgZHBpPTMwMCkKcGx0CmBgYAoKIyMgc29sZXVzCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQpwY2EgPC0gcHJjb21wKHQoY291bnRzLnNvbGV1cy50b3ApLCBzY2FsZS4gPSBUKQoKcGNhLmRhdGEgPC0gZGF0YS5mcmFtZShTYW1wbGUgPSByb3duYW1lcyhwY2EkeCksCiAgICAgICAgICAgICAgICAgICAgICAgWCA9IHBjYSR4WywgMV0sCiAgICAgICAgICAgICAgICAgICAgICAgWSA9IHBjYSR4WywgMl0pICU+JQogIG11dGF0ZSh0eXBlID0gc3Vic3RyKFNhbXBsZSwgMSwyKSkKCnBsdCA8LQogIGF1dG9wbG90KAogICAgcGNhLAogICAgZGF0YSA9IHBjYS5kYXRhLAogICAgY29sb3VyID0gJ3R5cGUnLAogICAgc2hhcGUgPSAxNywKICAgIGxhYmVsLnNob3cubGVnZW5kID0gRkFMU0UKICApICsKICBnZ3RpdGxlKCJQQ0Egc29sZXVzIikKIyBnZ3NhdmUoZmlsZW5hbWUgPSAiLi9wbG90cy8wNF9wY2Ffc29sZXVzX2ZpbHRlcmVkLnBuZyIsIHBsdCwgZHBpPTMwMCkKcGx0CmBgYAoKIyMgYm90aCB0aXNzdWVzCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQojIGNvbWJpbmUgYm90aCBjb3VudCB0YWJsZXMKY291bnRzLmJvdGggPC0KICBtZXJnZShjb3VudHMuZ2FzdHJvYywKICAgICAgICBjb3VudHMuc29sZXVzLAogICAgICAgIGJ5ID0gMCwKICAgICAgICBzdWZmaXhlcyA9IGMoIl9nYXN0cm9jIiwgIl9zb2xldXMiKSkgJT4lCiAgdGliYmxlOjpjb2x1bW5fdG9fcm93bmFtZXMoIlJvdy5uYW1lcyIpCgpwY2EgPC0gcHJjb21wKHQoY291bnRzLmJvdGgpLCBzY2FsZS4gPSBUKQoKcGNhLmRhdGEgPC0gZGF0YS5mcmFtZShTYW1wbGUgPSByb3duYW1lcyhwY2EkeCksCiAgICAgICAgICAgICAgICAgICAgICAgWCA9IHBjYSR4WywgMV0sCiAgICAgICAgICAgICAgICAgICAgICAgWSA9IHBjYSR4WywgMl0pICU+JQogIG11dGF0ZSh0eXBlID0gc3Vic3RyKFNhbXBsZSwgMSwgMiksCiAgICAgICAgIHRpc3N1ZSA9IHN0cmluZ3I6OnN0cl9leHRyYWN0KFNhbXBsZSwiWzphbHBoYTpdKyQiKSkKCnBsdCA8LQogIGF1dG9wbG90KAogICAgcGNhLAogICAgZGF0YSA9IHBjYS5kYXRhLAogICAgY29sb3VyID0gJ3R5cGUnLAogICAgc2hhcGUgPSAndGlzc3VlJywKICAgIGxhYmVsLnNob3cubGVnZW5kID0gRkFMU0UKICApICsKICBnZ3RpdGxlKCJQQ0EgYm90aCB0aXNzdWVzIikKIyBnZ3NhdmUoZmlsZW5hbWUgPSAiLi9wbG90cy8wNF9wY2FfYm90aF90aXNzdWVzX2ZpbHRlcmVkLnBuZyIsIHBsdCwgZHBpPTMwMCkKcGx0CmBgYAoKIyBERVNlcQoKIyMgcHJlcGFyaW5nIEREUyBvYmplY3RzCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpjcmVhdGVERFNPYmplY3QgPC0gZnVuY3Rpb24oY291bnRzLCBtZXRhZGF0YSkgewogICMgc2VsZWN0IHNhbXBsZSBjb2x1bW5zCiAgcmVvcmRlcl9pbmRleCA8LSBtYXRjaChyb3duYW1lcyhtZXRhZGF0YSksIGNvbG5hbWVzKGNvdW50cykpCiAgY291bnRzIDwtIGNvdW50c1sscmVvcmRlcl9pbmRleF0KICAKICAjIENoZWNrIG1ldGFkYXRhIGNvbnNpc3RlbmN5CiAgYWxsKHJvd25hbWVzKG1ldGFkYXRhKSAlaW4lIGNvbG5hbWVzKGNvdW50cykpICU+JQogICAgYXNzZXJ0dGhhdDo6YXNzZXJ0X3RoYXQoLiwgbXNnID0gIm1ldGFkYXRhIGFuZCBjb3VudCB0YWJsZSBkbyBub3QgbWF0Y2giKQogIAogICMjIERFU2VxMiBvYmplY3QKICBkZHMgPC0gREVTZXFEYXRhU2V0RnJvbU1hdHJpeChjb3VudERhdGEgPSBjb3VudHMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sRGF0YSA9IG1ldGFkYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2lnbiA9IH4gZ2Vub3R5cGUpCiAgcmV0dXJuKCBERVNlcShkZHMpICkKfQoKIyBjcmVhdGluZyBERVNlcSBPYmplY3RzCmRkcy5nYXN0cm9jIDwtIGNyZWF0ZUREU09iamVjdChjb3VudHMuZ2FzdHJvYywgbWV0YWRhdGEuZ2FzdHJvYykKZGRzLnNvbGV1cyA8LSBjcmVhdGVERFNPYmplY3QoY291bnRzLnNvbGV1cywgbWV0YWRhdGEuc29sZXVzKQpgYGAKCiMjIE1BLVBsb3RzCgojIyMgZ2FzdHJvYwoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KcGxvdE1BKGRkcy5nYXN0cm9jKQpgYGAKCgojIyMgc29sZXVzCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpwbG90TUEoZGRzLnNvbGV1cykKYGBgCgojIyBEaXNwZXJzaW9uIEVzdGltYXRlcwoKIyMjIGdhc3Ryb2MKCmBgYHtyfQpwbG90RGlzcEVzdHMoZGRzLmdhc3Ryb2MpCmBgYAoKIyMjIHNvbGV1cwoKYGBge3J9CnBsb3REaXNwRXN0cyhkZHMuc29sZXVzKQpgYGAKCiMjIFJlc3VsdHMKCmBgYHtyfQpyZXMuZ2FzdHJvYyA8LSByZXN1bHRzKGRkcy5nYXN0cm9jLCBhbHBoYSA9IDAuMDUpCnJlcy5zb2xldXMgPC0gcmVzdWx0cyhkZHMuc29sZXVzLCBhbHBoYSA9IDAuMDUpCgoKaGVhZChyZXMuZ2FzdHJvYykKaGVhZChyZXMuc29sZXVzKQpgYGAKCiMjIyB0b3Agc2lnbmlmaWNhbnQgZGlmZi4gZ2VuZXMKCmBgYHtyfQp0b3BHZW5lcy5nYXN0cm9jIDwtIGFzLmRhdGEuZnJhbWUocmVzLmdhc3Ryb2MpICU+JQogIHRpYmJsZTo6cm93bmFtZXNfdG9fY29sdW1uKCJHZW5lSUQiKSAlPiUKICB0b3BfbigxMDAsIHd0ID0gLXBhZGopICU+JQogIGFycmFuZ2UocGFkaikKCnRvcEdlbmVzLnNvbGV1cyA8LSBhcy5kYXRhLmZyYW1lKHJlcy5zb2xldXMpICU+JQogIHRpYmJsZTo6cm93bmFtZXNfdG9fY29sdW1uKCJHZW5lSUQiKSAlPiUKICB0b3BfbigxMDAsIHd0ID0gLXBhZGopICU+JQogIGFycmFuZ2UocGFkaikKCmtuaXRyOjprYWJsZShoZWFkKHRvcEdlbmVzLmdhc3Ryb2MpLCBjYXB0aW9uID0gImdhc3Ryb2MiKQprbml0cjo6a2FibGUoaGVhZCh0b3BHZW5lcy5zb2xldXMpLCBjYXB0aW9uID0gInNvbGV1cyIpCmBgYAojIyMgcC12YWx1ZXMKCmBgYHtyfQpoaXN0KHJlcy5nYXN0cm9jJHB2YWx1ZSwgbWFpbiA9ICJnYXN0cm9jIikKaGlzdChyZXMuc29sZXVzJHB2YWx1ZSwgbWFpbiA9ICJzb2xldXMiKQpgYGAKCiMjIyBzaHJpbmtpbmcKSXMgc2hyaW5raW5nIG5lY2Vzc2FyeT8KCltdIHdhdGNoIHRoZSBjb3Vyc2UgdmlkZW8KCgoKCiMjIFZvbGNhbm8gUGxvdAoKYGBge3J9CnJlc1RhYi5nYXN0cm9jIDwtIGFzLmRhdGEuZnJhbWUocmVzLmdhc3Ryb2MpICU+JQogIHRpYmJsZTo6cm93bmFtZXNfdG9fY29sdW1uKCJHZW5lSUQiKSAlPiUgCiAgIyBsZWZ0X2pvaW4oZW5zZW1ibEFubm90LCBieSA9ICJHZW5lSUQiKSAlPiUgCiAgZHBseXI6OnJlbmFtZShsb2dGQyA9IGxvZzJGb2xkQ2hhbmdlLCBGRFIgPSBwYWRqKSAlPiUKICBtdXRhdGUoYC1sb2cxMChwdmFsdWUpYCA9IC1sb2cxMChwdmFsdWUpKQoKcmVzVGFiLnNvbGV1cyA8LSBhcy5kYXRhLmZyYW1lKHJlcy5zb2xldXMpICU+JQogIHRpYmJsZTo6cm93bmFtZXNfdG9fY29sdW1uKCJHZW5lSUQiKSAlPiUgCiAgIyBsZWZ0X2pvaW4oZW5zZW1ibEFubm90LCBieSA9ICJHZW5lSUQiKSAlPiUgCiAgZHBseXI6OnJlbmFtZShsb2dGQyA9IGxvZzJGb2xkQ2hhbmdlLCBGRFIgPSBwYWRqKSAlPiUgCiAgbXV0YXRlKGAtbG9nMTAocHZhbHVlKWAgPSAtbG9nMTAocHZhbHVlKSkKYGBgCgpgYGB7cn0KZ2dwbG90KHJlc1RhYi5nYXN0cm9jLCBhZXMoeCA9IGxvZ0ZDLCB5ID0gYC1sb2cxMChwdmFsdWUpYCkpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvdXIgPSBGRFIgPCAwLjA1KSwgc2l6ZSA9IDEpICsKICBnZW9tX3RleHQoZGF0YSA9IH50b3BfbigueCwgMSwgd3Q9LUZEUiksIGFlcyhsYWJlbCA9IEdlbmVJRCkpCgpnZ3Bsb3QocmVzVGFiLnNvbGV1cywgYWVzKHggPSBsb2dGQywgeSA9IGAtbG9nMTAocHZhbHVlKWApKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3VyID0gRkRSIDwgMC4wNSksIHNpemUgPSAxKSArCiAgZ2VvbV90ZXh0KGRhdGEgPSB+dG9wX24oLngsIDEsIHd0PS1GRFIpLCBhZXMobGFiZWwgPSBHZW5lSUQpKQpgYGAKCgojIHNhdmUgUiBvamVjdHMKCmBgYHtyfQpzYXZlUkRTKGNvdW50cy5nYXN0cm9jLCBmaWxlID0gIi4vZGF0YS9Sb2JqZWN0cy9jb3VudHMuZ2FzdHJvYy5yZHMiKQpzYXZlUkRTKGNvdW50cy5zb2xldXMsIGZpbGUgPSAiLi9kYXRhL1JvYmplY3RzL2NvdW50cy5zb2xldXMucmRzIikKc2F2ZShkZHMuZ2FzdHJvYywgZGRzLnNvbGV1cywgZmlsZSA9ICIuL2RhdGEvUm9iamVjdHMvMDFfRERTLlJEYXRhIikKYGBgCgoKCiMgRmluYWwgUXVlc3Rpb25zCk1vc3QgdmFyaWFibGUgZ2VuZXM6CgoqIHdoaWNoIGhlYXRtYXAgZnVuY3Rpb24gc2hvdWxkIEkgdXNlIGFuZCB3aGF0IHNob3VsZCBJIGNoYW5nZSBhYm91dCB0aGUgcGxvdHM/Ciogc2hvdWxkIEkgaW5jbHVkZSB0aGUgdHJhY2VzPwoqIHNob3VsZCBJIGNvbXBsZXRlbHkgb21pdCB0aGUgaGVhdG1hcHMgKHZpc3VhbGl6c2F0aW9uIG9mIG1vc3QgdmFyaWFibGUgZ2VuZXM/KQoKUENBOgoKKiBJIHdhcyBub3Qgc3VyZSBhbnltb3JlIGlmIHRoZSBQQ0Egc2hvdWxkIGJlIHBlcmZvcm1lZCB3aXRoIG5vcm1hbGl6ZWQgb3Igbm90IG5vcm1hbGl6ZWQgY291bnRzPyAoTGFzdCB3ZWVrIEkgdGhvdWdodCB3ZSBzYWlkIG5vdCBub3JtYWxpemVkLCBidXQgdG9kYXkgSSB0aGluayB5b3Ugc2FpZCBub3JtYWxpemVkPykgSnVzdCB3YW50IHRvIGRvdWJsZSBjaGVjaywgc29ycnkuCiogaWYgdGhlIGRhdGEgaXMgbm90IG5vcm1hbGl6ZWQsIGRvZXMgaXQgbWVhbiB0byB0aGVuIHVzZSB0aGUgYHNjYWxlPVRgIGFyZ3VtZW50PwoKREVTZXE6CgoqIGlzIHNocmlua2luZyBuZWNlc3Nhcnk/IChgbGZjU2hyaW5rYCk=